﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Collections;


namespace _02_ModbusTcpClient_1._0._1
{
    public partial class Form1 : Form
    {
        Socket socket;
        bool connected;
        bool continueRead;
        public Form1()
        {
            InitializeComponent();

            TbxIpAddress.MouseEnter += MyMouseEnter;
            TbxPort.MouseEnter += MyMouseEnter;
            TbxUnit.MouseEnter += MyMouseEnter;
            TbxFunction.MouseEnter += MyMouseEnter;
            TbxStartAddress.MouseEnter += MyMouseEnter;
            TbxCount.MouseEnter += MyMouseEnter;
            BtnHelp.MouseEnter += MyMouseEnter;
            BtnClose.MouseEnter += MyMouseEnter;
            BtnMin.MouseEnter += MyMouseEnter;


            uiPanel1.MouseDown += Panel_MouseDown;
            uiPanel1.MouseMove += Panel_MouseMove;
        }
        #region 窗体移动
        //记录鼠标左键点击之前的位置
        Point point;
        void Panel_MouseDown(object sender, MouseEventArgs e)
        {
            //记录鼠标点击时的位置
            point = new Point(e.X, e.Y);
        }
        void Panel_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Point nowPosition = MousePosition;
                nowPosition.Offset(-point.X, -point.Y);
                this.DesktopLocation = nowPosition;
            }
        }
        #endregion
        #region 窗体最小化
        /// <summary>
        /// 窗体最小化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnMin_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }
        #endregion
        #region  窗体淡出效果
        /// <summary>
        /// 定时运行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnClose_Click(object sender, EventArgs e)
        {
            CloseTimer.Start();
        }

        private void CloseTimer_Tick(object sender, EventArgs e)
        {
            //改变当前窗体透明度
            if (this.Opacity > 0.025)
            {
                this.Opacity -= 0.025;
            }
            else
            {
                CloseTimer.Stop();
                this.Close();
            }
        }
        #endregion
        #region 调用帮助窗体
        private void BtnHelp_Click(object sender, EventArgs e)
        {
            helpWindows singleHelpWindows = helpWindows.GetHelpWindows();
            singleHelpWindows.Show();
        }

        #endregion
        #region 初始化
        /// <summary>
        /// 初始化部分参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            BtnDisconnect.Enabled = false;
            TbxIpAddress.Text = "127.0.0.1";
            TbxPort.Text = "502";
            TbxUnit.Text = "1";
        }
        #endregion
        #region 建立连接&断开连接&防断线
        /// <summary>
        /// 建立连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnConnect_Click(object sender, EventArgs e)
        {
            try
            {

                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(System.Net.IPAddress.Parse(TbxIpAddress.Text), Convert.ToInt32(TbxPort.Text));
                
                if (socket.Connected)
                {
                    MessageBox.Show("已连接服务端");
                    timer.Start();
                    connected = true;
                }
                BtnConnect.Enabled = false;
                BtnDisconnect.Enabled = true;

            }
            catch
            {
                MessageBox.Show("连接失败，请检查ip地址以及端口号是否填写正确\r\n  \t或者服务端是否正常开启");
            }

        }
        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnDisconnect_Click(object sender, EventArgs e)
        {
            //禁用收发，确保close前，已全部接收或发送
            timer.Stop();
            socket.Shutdown(SocketShutdown.Both);
            socket.Disconnect(true);
            socket.Close();

            if (!socket.Connected)
            {
                MessageBox.Show("已断开服务端");
                connected = false;
            }
            BtnConnect.Enabled = true;
            BtnDisconnect.Enabled = false;
        }
        /// <summary>
        /// 定时发送给服务器端，避免断线
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer_Tick(object sender, EventArgs e)
        {
            if (connected)
            {
                byte[] buffer;
                buffer = SendMessage(8, 0, 1);
                socket.Send(buffer);
                byte[] receiveBuffer = new byte[1024];
                socket.Receive(receiveBuffer);
            }
        }
        #endregion
        #region 客户端发送报文

        /// <summary>
        /// 客户端发送报文
        /// </summary>
        /// <param name="function"></param>
        /// <param name="startAddress"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        byte[] SendMessage(byte function, ushort startAddress, ushort count)
        {
            /*                  modbus tcp报文 --- 报文高位在前  --- C#为小端字节序
             *  MBAP:   
             *          1.事务元标识符： 占两个字节  客户端发送 这里设置为 0x00 0x01
             *          2.协议标识符：   占两个字节  modbus 规定为  0x00 0x00
             *          3.长度：        从单元标识符开始往后计数 占两个字节   客户端发送 规定为 0x00 0x06
             *          4.单元标识符：   替代RTU的从站地址， 占一个字节     默认为0x01
             * 
             *  其余报文： 
             *          1.功能码：    一个字节
             *              01：读线圈 读线圈状态 位操作
                            02：读离散量输入状态  位操作
                            03：读保持寄存器（每个寄存器含有两个字节）  字操作
                            04：读输入寄存器      字操作
                            05：写单个线圈
                            06：写单个寄存器
                            15：用于写多个线圈
                            16：写多个寄存器
             *          2.操作数据起始地址：  两个字节
             *          3.操作数据数量：     两个字节
             */

            byte[] buffer = new byte[12];

            buffer[0] = 0x00;
            buffer[1] = 0x01;
            buffer[2] = 0x00;
            buffer[3] = 0x00;
            buffer[4] = 0x00;
            buffer[5] = 0x06;
            buffer[6] = 0x01;
            buffer[7] = function;
            //起始地址
            buffer[8] = BitConverter.GetBytes(startAddress)[1];
            buffer[9] = BitConverter.GetBytes(startAddress)[0];
            //操作个数
            buffer[10] = BitConverter.GetBytes(count)[1];
            buffer[11] = BitConverter.GetBytes(count)[0];

            return buffer;
        }

        /// <summary>
        /// 写入多个coils or register时使用的方法重载
        /// </summary>
        /// <param name="function"></param>
        /// <param name="startAddress"></param>
        /// <param name="count"></param>
        /// <param name="byteCount"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        byte[] SendMessage(byte function, ushort startAddress, ushort count, byte byteCount, byte[] values)
        {
            /*                  modbus tcp报文 --- 报文高位在前  --- C#为小端字节序
             *  MBAP:   
             *          1.事务元标识符： 占两个字节  客户端发送 这里设置为 0x00 0x01
             *          2.协议标识符：   占两个字节  modbus 规定为  0x00 0x00
             *          3.长度：        从单元标识符开始往后计数 占两个字节   客户端发送 规定为 0x00 0x06
             *          4.单元标识符：   替代RTU的从站地址， 占一个字节     默认为0x01
             * 
             *  其余报文： 
             *          1.功能码：    一个字节
             *              01：读线圈 读线圈状态 位操作
                            02：读离散量输入状态  位操作
                            03：读保持寄存器（每个寄存器含有两个字节）  字操作
                            04：读输入寄存器      字操作
                            05：写单个线圈
                            06：写单个寄存器
                            15：用于写多个线圈
                            16：写多个寄存器
             *          2.操作数据起始地址：  两个字节
             *          3.操作数据数量：     两个字节
             */

            byte[] buffer = new byte[13 + values.Length];

            buffer[0] = 0x00;
            buffer[1] = 0x01;
            buffer[2] = 0x00;
            buffer[3] = 0x00;
            buffer[4] = 0x00;
            buffer[5] = Convert.ToByte(7 + values.Length);
            buffer[6] = 0x01;
            buffer[7] = function;
            //起始地址
            buffer[8] = BitConverter.GetBytes(startAddress)[1];
            buffer[9] = BitConverter.GetBytes(startAddress)[0];
            //操作个数
            buffer[10] = BitConverter.GetBytes(count)[1];
            buffer[11] = BitConverter.GetBytes(count)[0];
            //字节数量
            buffer[12] = byteCount;
            //字节数值
            for (int i = 0; i < values.Length; i++)
            {
                buffer[13 + i] = values[i];
            }
            return buffer;
        }
        #endregion
        #region 提示文本框
        /// <summary>
        /// 鼠标进入tbx框展现文本提示框
        /// </summary>
        /// <param name="sender">控件</param>
        /// <param name="e"></param>
        void MyMouseEnter(object sender, EventArgs e)
        {
            ToolTip toolTip = new ToolTip();
            string str = "";
            switch ((sender as Control).Name)
            {
                case "TbxIpAddress":
                    str = "请输入服务端IP地址";
                    break;
                case "TbxPort":
                    str = "请输入服务端Port端口号";
                    break;
                case "TbxUnit":
                    str = "请输入服务端单元标识符,如 01 02 03...";
                    break;
                case "TbxFunction":
                    str = "请输入功能码,如01 02 03...";
                    break;
                case "TbxStartAddress":
                    str = "请输入操作起始地址,如0 1 2...";
                    break;
                case "TbxCount":
                    str = "请输入操作个数";
                    break;
                case "BtnHelp":
                    str = "使用说明";
                    break;
                case "BtnMin":
                    str = "窗体最小化";
                    break;
                case "BtnClose":
                    str = "关闭窗体";
                    break;

            }
            toolTip.Show(str, (sender as Control), (sender as Control).Width, (sender as Control).Height, 1000);
        }
        #endregion
        #region  读取部分
        /// <summary>
        /// 读取server数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnReadData_Click(object sender, EventArgs e)
        {
            //因为采用了自动读取，所以这里发布了一个任务执行下面代码
            Task.Run(new Action(() =>
            {
                try
                {
                    continueRead = !continueRead;
                    if (continueRead)
                    {
                        while (continueRead)
                        {
                            //因为对前端ui画面做了更改，所以这里使用this.invoke回归主线程更改Ui控件画面
                            this.Invoke(new Action(() =>
                            {
                                TbxMessage.Clear();
                                BtnReadData.Style = Sunny.UI.UIStyle.LayuiRed;
                                BtnReadData.Text = "StopReadData";
                                switch (Convert.ToInt16(TbxFunction.Text))
                                {
                                    case 1:
                                        ReadCoil();    //读取单个输出线圈
                                        break;
                                    case 2:
                                        ReadInputBit();   //读取输入Bool
                                        break;
                                    case 3: 
                                        ReadRegister();    //读取保持性寄存器
                                        break;
                                    case 4:
                                        ReadInputRegister();   //读取输入寄存器
                                        break;
                                    default:
                                        MessageBox.Show("请检查功能码输入是否正确");
                                        break;
                                }
                            }));
                            System.Threading.Thread.Sleep(1000);   //1s读取一次

                        }
                    }
                    else
                    {
                        //回归主线程对UI控件进行变更
                        this.Invoke(new Action(() =>
                        {
                            BtnReadData.Text = "StartReadData";
                            BtnReadData.Style = Sunny.UI.UIStyle.Blue;
                        }));

                    }
                }
                catch
                {
                    if (socket.Connected)
                    {
                        MessageBox.Show("请正确输入功能码以及相关参数");
                    }
                    else
                    {
                        MessageBox.Show("请先建立连接");
                    }
                }


            }));
        }


        void BBB()
        {

        }


        /// <summary>
        /// 读输出线圈 功能码01
        /// </summary>
        void ReadCoil()
        {
            //声明buffer数值，用于存储发送字节
            byte[] buffer;
            //调用SendMessage方法，返回发送字节
            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];

            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;
            }


            int receiveDataLength = (receiveBuffer[4] * 256) + receiveBuffer[5] - 3; //获取返回的有效数据字节数

            byte[] receiveDataBuffer = new byte[receiveDataLength];  //存放接收的有效数据字节
            Array.Copy(receiveBuffer, 9, receiveDataBuffer, 0, receiveDataLength); //receiveBuffer中，前面九个字节为MBAP+功能码+返回的字节数量，所以要从第十个字节开始拿去数据

            ///因为读取coil为单个位，不可以用字节来表示了，所以注释掉
            //for (int i = 0; i < receiveDataLength; i++)
            //{
            //    for (int j = 0; j < 7; j++)
            //        TbxMessage.AppendText($"第{i}字节位数值为：" + BitConverter.ToBoolean(receiveDataBuffer, j) + "\r\n");
            //}

            BitArray boolArray = new BitArray(receiveDataBuffer);
            for (int i = 0; i < boolArray.Length; i++)
            {
                if (i % 2 == 0)
                {
                    TbxMessage.AppendText($"读取第{i + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + boolArray[i] + "\t  | \t");
                }
                else
                {
                    TbxMessage.AppendText($"读取第{i + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + boolArray[i] + "\r\n");
                }
            }

        }


        /// <summary>
        /// 读取输入位 功能码02
        /// </summary>
        void ReadInputBit()
        {
            byte[] buffer;
            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];

            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;

            }


            int receiveDataLength = (receiveBuffer[4] * 256) + receiveBuffer[5] - 3; //获取返回的有效数据字节数

            byte[] receiveDataBuffer = new byte[receiveDataLength];  //存放接收的有效数据字节
            Array.Copy(receiveBuffer, 9, receiveDataBuffer, 0, receiveDataLength); //receiveBuffer中，前面九个字节为MBAP+功能码+返回的字节数量，所以要从第十个字节开始拿去数据


            BitArray boolArray = new BitArray(receiveDataBuffer);
            for (int i = 0; i < boolArray.Length; i++)
            {
                if (i % 2 == 0)
                {
                    TbxMessage.AppendText($"读取第{i + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + boolArray[i] + "\t  | \t");
                }
                else
                {
                    TbxMessage.AppendText($"读取第{i + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + boolArray[i] + "\r\n");
                }
            }




        }

        /// <summary>
        /// 读取寄存器 功能码03
        /// </summary>
        void ReadRegister()
        {
            byte[] buffer;
            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];

            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;
            }

            int receiveDataLength = (receiveBuffer[4] * 256) + receiveBuffer[5] - 3; //获取返回的有效数据字节数

            byte[] receiveDataBuffer = new byte[receiveDataLength];  //存放接收的有效数据字节

            Array.Copy(receiveBuffer, 9, receiveDataBuffer, 0, receiveDataLength); //receiveBuffer中，前面九个字节为MBAP+功能码+返回的字节数量，所以要从第十个字节开始拿去数据

            //byte[] reverseDataBuffer = receiveDataBuffer.Reverse().ToArray();  //此处没必要反转，因为for里面，展示当前第几位不好与实际数值匹配，for内部直接采取IPAddress.NetWorlToHostOrder进行大小端反转

            for (int i = 0; i < receiveDataLength; i += 2)
            {
                if ((i / 2) % 2 == 0)
                {
                    //因为数据是两个字节为单位，所以i/2 ，接收到的报文是大端在前，而C#是小端在前模式，直接使用IPAddress.NetWorkToHostOrder进行数值大小端转换
                    TbxMessage.AppendText($"读取第{i / 2 + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + IPAddress.NetworkToHostOrder(BitConverter.ToInt16(receiveDataBuffer, i)).ToString() + "\t  | \t");
                }
                else
                {
                    //因为数据是两个字节为单位，所以i/2 ，接收到的报文是大端在前，而C#是小端在前模式，直接使用IPAddress.NetWorkToHostOrder进行数值大小端转换
                    TbxMessage.AppendText($"读取第{i / 2 + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + IPAddress.NetworkToHostOrder(BitConverter.ToInt16(receiveDataBuffer, i)).ToString() + "\r\n");
                }
            }
        }


        /// <summary>
        /// 读取输入寄存器 功能码04
        /// </summary>
        void ReadInputRegister()
        {
            byte[] buffer;
            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];

            socket.Receive(receiveBuffer);


            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;
            }

            int receiveDataLength = (receiveBuffer[4] * 256) + receiveBuffer[5] - 3; //获取返回的有效数据字节数

            byte[] receiveDataBuffer = new byte[receiveDataLength];  //存放接收的有效数据字节

            Array.Copy(receiveBuffer, 9, receiveDataBuffer, 0, receiveDataLength); //receiveBuffer中，前面九个字节为MBAP+功能码+返回的字节数量，所以要从第十个字节开始拿去数据

            //byte[] reverseDataBuffer = receiveDataBuffer.Reverse().ToArray();  //此处没必要反转，因为for里面，展示当前第几位不好与实际数值匹配，for内部直接采取IPAddress.NetWorlToHostOrder进行大小端反转

            for (int i = 0; i < receiveDataLength; i += 2)
            {
                if ((i / 2) % 2 == 0)
                {
                    //因为数据是两个字节为单位，所以i/2 ，接收到的报文是大端在前，而C#是小端在前模式，直接使用IPAddress.NetWorkToHostOrder进行数值大小端转换
                    TbxMessage.AppendText($"读取第{i / 2 + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + IPAddress.NetworkToHostOrder(BitConverter.ToInt16(receiveDataBuffer, i)).ToString() + "\t  | \t");
                }
                else
                {
                    //因为数据是两个字节为单位，所以i/2 ，接收到的报文是大端在前，而C#是小端在前模式，直接使用IPAddress.NetWorkToHostOrder进行数值大小端转换
                    TbxMessage.AppendText($"读取第{i / 2 + Convert.ToInt32(TbxStartAddress.Text)}位数值为：" + IPAddress.NetworkToHostOrder(BitConverter.ToInt16(receiveDataBuffer, i)).ToString() + "\r\n");
                }
            }
        }


        #endregion
        #region 写入部分
        /// <summary>
        /// 写入功能码
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnWriteData_Click(object sender, EventArgs e)
        {
            try
            {
                switch (Convert.ToInt16(TbxFunction.Text))
                {
                    case 5:
                        WriteSingleCoil();
                        break;
                    case 6:
                        WriteRegister();
                        break;
                    case 15:
                        WriteCoils();
                        break;
                    case 16:
                        WriteRegisters();
                        break;
                    default:
                        MessageBox.Show("请检查功能码输入是否正确");
                        break;
                }
            }
            catch
            {
                if (socket.Connected)
                {
                    MessageBox.Show("请正确输入功能码以及相关参数");
                }
                else
                {
                    MessageBox.Show("请先建立连接");
                }

            }
        }
        /// <summary>
        /// 写入线圈 功能码05  报文注意： 0xff00 置1   0x0000置0  Message:MBAP+  0x05+线圈地址H+线圈地址L+线圈值H+线圈值L
        /// </summary>
        void WriteSingleCoil()
        {
            //对于写入线圈coil， 报文 0xff00为置1  0x0000为置0
            byte[] buffer;
            byte[] coilValue = new byte[2];
            if (Convert.ToUInt16(TbxCount.Text) == 1)
            {
                coilValue[1] = 0xff;
                coilValue[0] = 0x00;
            }
            else
            {
                coilValue[1] = 0x00;
                coilValue[0] = 0x00;
            }

            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), BitConverter.ToUInt16(coilValue, 0));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];
            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;
            }

            MessageBox.Show("写入成功");

        }

        /// <summary>
        /// WriteSingleRegister Function:06   Message: MBAP+ 0x06+寄存器地址H+寄存器地址L+寄存器值H+寄存器值L
        /// </summary>
        void WriteRegister()
        {
            byte[] buffer;
            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text));
            socket.Send(buffer);

            byte[] receiveBuffer = new byte[1024];
            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("客户端功能码与服务端功能码不同，请进行检查");
                return;
            }
            MessageBox.Show("写入成功");
        }



        /// <summary>
        /// WriteCoils  Function:15   Message: MBAP(the length will be change) + 0x15 +寄存器地址H+寄存器地址L+Bool输出数量H+Bool输出数量L + ByteCount(输出数量%8==0？输出数量：输出数量+1) + Values[输出数量%8==0？输出数量：输出数量+1]
        /// </summary>
        void WriteCoils()
        {
            byte[] buffer;

            //get the byteCount
            byte bytecount = Convert.ToByte(Convert.ToUInt16(TbxCount.Text) % 8 == 0 ? Convert.ToUInt16(TbxCount.Text) / 8 : (Convert.ToUInt16(TbxCount.Text) / 8) + 1);


            string[] str = TbxMessage.Text.Split(',');

            //get the valuesLength
            byte[] values = new byte[Convert.ToUInt16(TbxCount.Text) % 8 == 0 ? Convert.ToUInt16(TbxCount.Text) / 8 : (Convert.ToUInt16(TbxCount.Text) / 8) + 1];
            bool[] bools = new bool[values.Length * 8];


            //这里使用BitArray，可以对位进行操作 ，先对位进行操作，然后再将该对象copy给byte[]即可
            BitArray bitArray = new BitArray(values.Length * 8);

            //ushort iii = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == "1")
                {
                    bools[i] = true;
                    bitArray.Set(i, true);
                }
                else
                {
                    bools[i] = false;
                    bitArray.Set(i, false);
                }
            }
            //BitArray.CopyTo 方法，将目标数组的指定索引处开始将整个BitArray复制到兼容的一维数组
            // 重点：这个复制，是将会按位去复制到目标数组！！！！！！！
            bitArray.CopyTo(values, 0);

            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text), bytecount, values);
            socket.Send(buffer);


            //byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0A, 0x02, 0xCD, 0x01 };
            //socket.Send(data);
            //byte[] bbb = new byte[1024];
            //int aaa = socket.Receive(bbb);


            byte[] receiveBuffer = new byte[1024];
            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("1.客户端功能码与服务端功能码不同，请进行检查\r\n2.服务器端数据地址未开放，请检查");
                return;
            }
            MessageBox.Show("写入成功");
        }


        /// <summary>
        /// WriteRegisters  Function:16  Message:MBAP+16+ 起始地址H+起始地址L+寄存器个数H+寄存器个数L+ byteCount(寄存器个数*2 // so the registerCount max value is 127) + values[(寄存器个数*2)]
        /// </summary>
        void WriteRegisters()
        {
            byte[] buffer;

            //get the byteCount
            byte bytecount = Convert.ToByte(Convert.ToUInt16(TbxCount.Text) * 2);

            //becuase the one str represent a word , so the valuesLength is double valueLength
            string[] str = TbxMessage.Text.Split(',');
            ushort[] value = new ushort[Convert.ToUInt16(TbxCount.Text)];
            byte[] values = new byte[Convert.ToUInt16(TbxCount.Text) * 2];


            //becuase the one str represent two byte ,so first change to ushort , than change to byte
            //use IPAddress.HostToNetWorkOrder, to rollback values

            for (int i = 0; i < str.Length; i++)
            {
                //声明一个临时byte数组，为了大小端对调  -----  因为重载方法 SendMessage里面 发送寄存器数值字节数组 是顺序赋值进去 先进排前面  但是modbus协议，是大端在前， 所以这里需要反转，不然给1的时候相当于给了256这样
                byte[] tempByte = new byte[2];

                tempByte[0] = BitConverter.GetBytes(Convert.ToUInt16(str[i]))[1];
                tempByte[1] = BitConverter.GetBytes(Convert.ToUInt16(str[i]))[0];

                //将对调的大小端复制给value，用于下面复制给values
                value[i] = BitConverter.ToUInt16(tempByte, 0);


                for (int j = 0; j < 2; j++)
                {
                    values[i * 2 + j] = BitConverter.GetBytes(value[i])[j];
                }
            }

            buffer = SendMessage(Convert.ToByte(TbxFunction.Text), Convert.ToUInt16(TbxStartAddress.Text), Convert.ToUInt16(TbxCount.Text), bytecount, values);

            socket.Send(buffer);



            byte[] receiveBuffer = new byte[1024];
            socket.Receive(receiveBuffer);

            if (!(receiveBuffer[7] == buffer[7]))
            {
                MessageBox.Show("1.客户端功能码与服务端功能码不同，请进行检查\r\n2.服务器端数据地址未开放，请检查");
                return;
            }
            MessageBox.Show("写入成功");
        }
        #endregion




    }
}
